/*
 * Main.cs - Wordtrainer
 * 
 * Copyright (C) 1998-2007 Markus Kilås
 *               
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 *
 * Authors:
 *   Markus Kilås <markus@markuspage.com>
 */
using System;
using System.Collections;
using Mono.Unix;

namespace WtSharp
{
	
	
	public class Practise
	{
		private IPractiseListener m_listener; // MainWindow
		private IPractiseForm m_form; // PractiseForm
		
		public int m_nPractiseIndex;
		public ArrayList m_PractiseWords;
		public PractiseInfo m_PractiseInfo;
		public int m_nThisWordWrong;
		public bool m_bInstantCorrection;
		public Random m_Random = new Random();
		private WtDocument m_document;
		public bool m_bMixWords;
		public bool m_bCaseSensitive;
		public uint m_nPauseTime;
		public bool m_bPlayWrong;
		public bool m_bPlayRight;
		public string m_RightSoundFile;
		public string m_WrongSoundFile;

		private Gdk.Pixbuf pixbufMarkS;
		private Gdk.Pixbuf pixbufMarkV;
		
		private bool inputDisabled = false;
		
		public Practise()
		{
			// Load marks
			pixbufMarkS = Gdk.Pixbuf.LoadFromResource("mark-s.png").ScaleSimple(16,16,Gdk.InterpType.Bilinear);
			pixbufMarkV = Gdk.Pixbuf.LoadFromResource("mark-v.png").ScaleSimple(16,16,Gdk.InterpType.Bilinear);
		}
		
		public IPractiseForm form
		{
			set 
			{
				m_form = value;
			}
			/*get
			{
				return m_form;
			}*/
		}
		
		public IPractiseListener listener
		{
			set 
			{
				m_listener = value;
			}
			/*get
			{
				return m_listener;
			}*/
		}
		
		public bool InstantCorrection
		{
			set
			{
				m_bInstantCorrection = value;
			}
			get
			{
				return m_bInstantCorrection;
			}
		}
		
		public WtDocument Document
		{
			set
			{
				m_document = value;
			}
		}
		
		public void StartPractise()
		{
			Console.WriteLine("start");
			m_nPractiseIndex = 0;
			m_PractiseInfo.Right = 0;
			m_PractiseInfo.Wrong = 0;
			
			// Mix
			m_PractiseWords = MixWords(m_PractiseWords);

			// ClearAnswerBox
			ArrayList alternatives = new ArrayList();
			foreach(WtWord word in m_document.Words)
			{
				alternatives.Add(m_PractiseInfo.PractiseF ? word.WordF : word.WordT);
			}
			m_form.FillAnswerAlternatives(alternatives);
			
			NextWord();
		}
		
		public void NextWord()
		{
			if(inputDisabled)
				return;
			
			if(m_nPractiseIndex >= m_PractiseInfo.WordCount)
			{
				Console.WriteLine("Finished");
				ArrayList wrongWords = new ArrayList();
				foreach(WtWord word in m_PractiseWords)
				{
					if(word.Wrong > 0)
					{
						word.Wrong = 0;
						wrongWords.Add(word);
					}
				}
				Console.WriteLine("before");
				inputDisabled = true;
				ResultDialog dlg = new ResultDialog(wrongWords, m_PractiseInfo);
				dlg.Modal = true;
				dlg.Run();
				inputDisabled = false;
				Console.WriteLine("After");
				if(dlg.Repeat)
				{
					m_PractiseWords = MixWords(wrongWords);
					m_PractiseInfo.WordCount = wrongWords.Count;
					StartPractise();
				}
				else
				{
					m_listener.PractiseFinished(); // Tell MainWindow so it can switch to edit
				}
				
				return;
			}
			else
				Console.WriteLine("not Finished");
			
			m_nThisWordWrong = 0;
			
			if(m_PractiseInfo.PractiseF)
				m_listener.HelpTextChanged(Catalog.GetString("Write the forign word and press ENTER"));
			else
				m_listener.HelpTextChanged(Catalog.GetString("Write the translation and press ENTER"));
			
			string question; 
			string[] words;
			if(m_PractiseInfo.PractiseF)
			{
				question = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordT;
				words = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsT;
			}
			else
			{
				question = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordF;
				words = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsF;
			}
			
			// Give a hint about how many synonyms the answer has
			if(words.Length > 1)
				question += " (" + words.Length + ")";
			
			m_form.UpdateInfo();
			
			Gdk.Pixbuf pixbuf = null;
			switch(((WtWord)m_PractiseWords[m_nPractiseIndex]).Type)
			{
			case 1:
				pixbuf = pixbufMarkS;
				break;
			case 2:
				pixbuf = pixbufMarkV;
				break;
			}
			m_form.SetQuestion(question, pixbuf);
		}
		
		private ArrayList MixWords(ArrayList words)
		{	
			if(!m_bMixWords)
				return words;
			
			ArrayList a1 = new ArrayList(words);
			ArrayList ret = new ArrayList(words.Count);
			
			while(a1.Count > 0)
			{
				object o = a1[m_Random.Next(a1.Count-1)];
				ret.Add(o);
				a1.Remove(o);
			}
			
			return ret;
		}
		
		public void Correct(string text)
		{
			if(inputDisabled)
				return;
			
			m_form.DisableInput(true); //PractiseOK.Sensitive = false;
			string correctWord, enteredWord, origCorrectWord;
			string[] correctWords;
			if(m_PractiseInfo.PractiseF)
			{
				correctWord = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordF;
				correctWords = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsF;
			}
			else
			{
				correctWord = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordT;
				correctWords = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsT;
			}
			
			enteredWord = text.Trim();
			origCorrectWord = correctWord;
			
			// If we ignore case
			if(!m_bCaseSensitive)
			{
				enteredWord = enteredWord.ToUpper();
				correctWord = correctWord.ToUpper();
			}
			
			if(enteredWord.Equals(correctWord) || SynonymsEquals(enteredWord, correctWords))
			{
				m_form.SetLabelAnswerMarkup(Catalog.GetString("<b>Answer</b>"));
				m_listener.HelpTextChanged(Catalog.GetString("Right answer!"));
				m_form.SetAnswerEntryBaseColor(new Gdk.Color(0, 255, 0));
				
				if(m_nThisWordWrong == 0)
					m_PractiseInfo.Right++;
				if(m_nThisWordWrong == 1)
					m_PractiseInfo.Right2nd++;
				
				m_form.UpdateInfo();
				m_nPractiseIndex++;
				if(m_bPlayRight)
					PlayRightSound();
				
				inputDisabled = true;
				GLib.Timeout.Add(m_nPauseTime+200, new GLib.TimeoutHandler(TimeoutNextWord));
			}
			else
			{
				m_nThisWordWrong++;
				
				if(m_nThisWordWrong >= 2) // Should we show the correct answer?
				{
					m_form.SetLabelAnswerMarkup(Catalog.GetString("The right answer is:"));
					m_form.SetAnswerEntryText(origCorrectWord);
					m_form.SetAnswerEntryBaseColor(new Gdk.Color(0, 255, 0));
					m_form.SetPractseOKLabel(Catalog.GetString("Continue"));
					m_listener.HelpTextChanged(Catalog.GetString("Wrong answer. Press ENTER to continue..."));
				}
				else
				{				
					m_form.SetAnswerEntryBaseColor(new Gdk.Color(255, 0, 0));	
					m_form.SetLabelAnswerMarkup(Catalog.GetString("<b>Try again:</b>"));
					
					m_listener.HelpTextChanged(Catalog.GetString("Wrong answer. Try again."));
					m_PractiseInfo.Wrong++;
				}
				m_form.DisableInput(false);
				((WtWord)m_PractiseWords[m_nPractiseIndex]).Wrong++;
				m_form.UpdateInfo();
				if(m_bPlayWrong)
					PlayWrongSound();
				
			}
		}
				
		private bool TimeoutNextWord()
		{
			ResetAnswer();
			inputDisabled = false;
			NextWord();
			return false;
		}

		private void ResetAnswer()
		{
			m_form.SetAnswerEntryBaseColor(new Gdk.Color(255, 255, 255));
			m_listener.HelpTextChanged("");
		}
		
		private bool SynonymEquals(string enteredWord, string[] correctWords)
		{
			string entered = enteredWord.Trim();
			foreach(string correct in correctWords)
			{
				string correctWord = correct;
				if(!m_bCaseSensitive)
				{
					enteredWord = enteredWord.ToUpper();
					correctWord = correctWord.ToUpper();
				}
				if(correctWord.Equals(entered))
					return true;
			}
			return false;
		}

		private bool SynonymsEquals(string enteredText, string[] correctWords)
		{
			Console.WriteLine("IsSynonymsEquals()");
			string[] enteredWords = enteredText.Split(',');
			int nCorrect = 0;
			
			foreach(string entered in enteredWords) // For all entered words
			{
				if(!SynonymEquals(entered, correctWords)) // check that it equals a correct one
					return false;
				nCorrect++;
			}
			return nCorrect >= 1;
		}
		
		private bool SynonymStartsWith(string enteredWord, string[] correctWords)
		{
			string entered = enteredWord.Trim();
			foreach(string correct in correctWords)
			{
				string correctWord = correct;
				if(!m_bCaseSensitive)
				{
					enteredWord = enteredWord.ToUpper();
					correctWord = correctWord.ToUpper();
				}
				if(correctWord.StartsWith(entered))
					return true;
			}
			return false;
		}
		
		private bool SynonymsStartsWith(string enteredText, string[] correctWords)
		{
			string[] enteredWords = enteredText.Split(',');
			
			foreach(string entered in enteredWords) // For all entered words
			{
				if(!SynonymStartsWith(entered, correctWords)) // check that it a correct one starts with it
					return false;
			}
			return true;
		}

		public void TextChanged(string enteredWord)
		{
			if(inputDisabled)
				return;
			
			m_form.SetAnswerEntryBaseColor(new Gdk.Color(255, 255, 255));
			m_form.SetPractseOKLabel(Catalog.GetString("OK"));
			
			if(m_bInstantCorrection)
			{
				string correctWord/*, enteredWord*/;
				string[] correctWords;
				if(m_PractiseInfo.PractiseF)
				{
					correctWord = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordF;
					correctWords = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsF;
				}
				else
				{
					correctWord = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordT;
					correctWords = ((WtWord)m_PractiseWords[m_nPractiseIndex]).WordsT;
				}
				
				//enteredWord = Answer.Entry.Text;
				
				// If we ignore case
				if(!m_bCaseSensitive)
				{
					enteredWord = enteredWord.ToUpper();
					correctWord = correctWord.ToUpper();
				}
				
				if(enteredWord.Equals(correctWord) || SynonymsEquals(enteredWord, correctWords))
				{
					Correct(enteredWord);
				}
				else if(!correctWord.StartsWith(enteredWord) && !SynonymsStartsWith(enteredWord, correctWords))
				{
					m_form.SetAnswerEntryBaseColor(new Gdk.Color(255, 0, 0));
				}
			}
		}
	
		private void PlayRightSound()
		{
			MainWindow.PlaySoundFile(m_RightSoundFile);
		}
		
		private void PlayWrongSound()
		{
			MainWindow.PlaySoundFile(m_WrongSoundFile);
		}
		
		
		
		
	}
}
